package com.win.oauth.dto;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.win.oauth.entity.Fun;
import com.win.oauth.entity.Module;

import java.util.*;
import java.util.stream.Collectors;

public class VueRouteDTO {

    @JsonIgnore
    private Long id;

    @JsonIgnore
    private Long parentId;

    private String name;

    private String title;

    private String icon;

    private String path;

    private String component;

    private String redirect;

    private Boolean hidden;

    private List<String> permissionCodes;

    private List<VueRouteDTO> children = new ArrayList<>();

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public Long getParentId() {
        return parentId;
    }

    public void setParentId(Long parentId) {
        this.parentId = parentId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getIcon() {
        return icon;
    }

    public void setIcon(String icon) {
        this.icon = icon;
    }

    public String getPath() {
        return path;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public String getComponent() {
        return component;
    }

    public void setComponent(String component) {
        this.component = component;
    }

    public String getRedirect() {
        return redirect;
    }

    public void setRedirect(String redirect) {
        this.redirect = redirect;
    }

    public Boolean getHidden() {
        return hidden;
    }

    public void setHidden(Boolean hidden) {
        this.hidden = hidden;
    }

    public List<String> getPermissionCodes() {
        return permissionCodes;
    }

    public void setPermissionCodes(List<String> permissionCodes) {
        this.permissionCodes = permissionCodes;
    }

    public List<VueRouteDTO> getChildren() {
        return children;
    }

    public void setChildren(List<VueRouteDTO> children) {
        this.children = children;
    }

    public static List<VueRouteDTO> build(List<? extends Module> menus, List<? extends Fun> funs) {
        menus = moduleSorted(menus);
        List<VueRouteDTO> temp = menus.stream().map(m -> {
            VueRouteDTO dto = new VueRouteDTO();
            dto.setId(m.getId());
            dto.setParentId(m.getParentId());
            dto.setName(m.getName());
            dto.setTitle(m.getTitle());
            dto.setIcon(m.getIcon());
            dto.setPath(m.getPath());
            dto.setComponent(m.getComponent());
            dto.setHidden(m.getHideInMenu() > 0);
            List<String> permissionCodes = new ArrayList<>();
            for (Fun f : funs) {
                if (Objects.equals(f.getModuleId(), m.getId())) {
                    permissionCodes.add(f.getPermissionCode());
                }
            }
            dto.setPermissionCodes(permissionCodes);
            return dto;
        }).collect(Collectors.toList());

        List<VueRouteDTO> routes = new ArrayList<>();
        for (VueRouteDTO t : temp) {
            if (Objects.isNull(t.getParentId())) {
                routes.add(findChildrenAndReturn(t, temp));
            }
        }
        for (VueRouteDTO t : temp) {
            if (!t.getChildren().isEmpty()) {
                t.setRedirect(findRedirect(t));
            }
        }
        return routes;
    }


    private static List<Module> moduleSorted(List<? extends Module> modules) {
        List<Module> list = new ArrayList<>();
        Map<Long, ? extends List<? extends Module>> map = modules.stream().collect(Collectors.groupingBy(el -> Objects.isNull(el.getParentId()) ? 0L : el.getParentId()));
        for (Map.Entry<Long, ? extends List<? extends Module>> entry : map.entrySet()) {
            List<? extends Module> ms = entry.getValue().stream().sorted((o1, o2) -> {
                Integer seq1 = o1.getSeq();
                Integer seq2 = o2.getSeq();
                if (Objects.nonNull(seq1) && Objects.nonNull(seq2)) {
                    return seq1 - seq2;
                }
                return (int) (o1.getId() - o2.getId());
            }).collect(Collectors.toList());
            list.addAll(ms);
        }
        return list;
    }

    private static VueRouteDTO findChildrenAndReturn(VueRouteDTO route, List<VueRouteDTO> routes) {
        for (VueRouteDTO t : routes) {
            if (route.getId().equals(t.getParentId())) {
                t.setPath(route.getPath() + t.getPath());
                route.getChildren().add(findChildrenAndReturn(t, routes));
            }
        }
        return route;
    }

    private static String findRedirect(VueRouteDTO route) {
        if (route.getChildren().isEmpty()) {
            return route.getPath();
        }
        VueRouteDTO child = route.getChildren().get(0);
        return findRedirect(child);
    }
}
